#include <iostream>
#include <cassert>
#include "SDL.h"
#include "foreach.h"

#include "cmdline/cmdline.h"
#include "util/wstr.h"
#include "util/exception.h"
#include "util/window.h"
#include "tests/runtests.h"

#include "object/object.h"

#include "tools/testobject.h"


struct InitError : util::Exception {
	InitError(std::wstring str) : util::Exception(str) {}
};

// Initializing
bool sdl_initialized = false;
bool glew_initialized = false;

void initLibs();
void deInitLibs();

// Commandline parameters
bool run_tests = true;
bool stop_parameter = false;
int window_width = 800;
int window_height = 480;
bool window_fullscreen = false;

void readCmdlineParams(int argc, char *argv[]);
void listVideoModes();
void showVideoInfo();
void runTests();
void parseResolution(std::wstring str);

int main(int argc, char *argv[]) {
	// Read commandline parameters
	try {
		readCmdlineParams(argc, argv);
	} catch(util::Exception &e) {
		std::wcout << e.what() << std::endl;
		return 1;
	}
	if(stop_parameter) return 0;

	// Unit tests
	if(run_tests) runTests();
	
	// Initialize
	try {
		initLibs();
		atexit(deInitLibs);
	} catch(util::Exception &e) {
		std::wcout << e.what() << std::endl;
		return 1;
	}

	// Run game
	try {
		// Game();
	} catch(util::Exception &e) {
		std::wcout << L"Unhandled exception: " << e.what() << std::endl;
		return 1;
	}

	return 0;
}

void initLibs() {
	// Init SDL
	if(SDL_Init(SDL_INIT_TIMER|SDL_INIT_AUDIO|SDL_INIT_VIDEO|SDL_INIT_JOYSTICK) != 0) {
		throw InitError(L"Couldn't init SDL: " + W(SDL_GetError()));
	} else sdl_initialized = true;

	// Open window
	uint32 default_bpp = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
	window::openWindow(window_width, window_height, default_bpp, window_fullscreen);

	// Init GLEW
	GLenum glew_errno = glewInit();
	if(glew_errno != GLEW_OK) {
		std::wstring glew_error = W((const char*)glewGetErrorString(glew_errno));
		throw InitError(L"Couldn't init GLEW: " + glew_error);
	} else glew_initialized = true;
	std::wstring glew_version = W((const char*)glewGetString(GLEW_VERSION));
	std::wcout << L"Status: Using GLEW " << glew_version << std::endl;
}

void deInitLibs() {
	if(sdl_initialized) SDL_Quit();
}

void readCmdlineParams(int argc, char *argv[]) {
	cmdline::CommandlineParser parser;
	parser.addOption(L'f', L"fullscreen", false, L"Sets fullscreen mode");
	parser.addOption(L'w', L"windowed", false, L"Sets windowed mode");
	parser.addOption(L'l', L"list-modes", false, L"Lists supported video modes");
	parser.addOption(L's', L"show-video-info", false, L"Show information about the video system");
	parser.addOption(L'n', L"no-tests", false, L"Don't run unit tests");
	parser.addOption(L'r', L"resolution", true, L"Use resolution (e.g. 1280x800)");
	
	parser.addOption(L'-', L"test-object", true, L"Validate and dump an object file, then exit");
	
	try {
		parser.parse(argc, argv);
	} catch(cmdline::UnknownParamException &) {
		std::wcout << parser.help();
		stop_parameter = true;
		return;
	}
	
	foreach(cmdline::Parameter *param, parser.getParams()) {
		std::wstring flag = param->option->strflag;
		if(flag == L"fullscreen") {
			window_fullscreen = true;
		} else
		if(flag == L"windowed") {
			window_fullscreen = false;
		} else
		if(flag == L"resolution") {
			parseResolution(param->wstr);
		} else
		if(flag == L"show-video-info") {
			showVideoInfo();
			stop_parameter = true;
		} else
		if(flag == L"list-modes") {
			listVideoModes();
			stop_parameter = true;
		} else
		if(flag == L"no-tests") {
			run_tests = false;
		} else
		if(flag == L"test-object") {
			tools::testObject(param->wstr);
			stop_parameter=true;
		}
		else {
			assert(false);
		}
	}

}

void listVideoModes() {
	initLibs();
	std::vector<window::Resolution> resolutions = window::listVideoModes();
	std::wcout << L"Supported resolutions:" << std::endl;
	foreach(window::Resolution &resolution, resolutions) {
		std::wcout << L"    " << resolution.w << L"x" << resolution.h << std::endl;
		// << L"x" << resolution.bpp << L"bit" << std::endl;
	}
	deInitLibs();
}

void showVideoInfo() {
	initLibs();
	std::wcout << window::getVideoInfo();
	deInitLibs();
}

void runTests() {
	tests::runTests();
}

void parseResolution(std::wstring str) {
	std::vector<std::wstring> parts = wstr::split(str, L"xX*,");
	if(parts.size() != 2) throw util::Exception(L"Couldn't parse resolution.");

	int new_width = wstr::toInt(parts[0]);
	int new_height = wstr::toInt(parts[1]);

	window_width = new_width;
	window_height = new_height;
}
